package com.nannybear.View;

import android.graphics.Color;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RadioButton;
import android.widget.SeekBar;
import android.widget.TextView;

import com.nannybear.Model.ProgressItem;
import com.nannybear.R;

import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

/**
 * \brief Baby state screen
 * \author Audrey LOISEAU
 * \date 16-03-2015
 */

/********************************* COPYRIGHT *********************************
 * Copyright (c) 2015, Audrey LOISEAU and Benjamin LANCINOT.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *************************************************************************************/

public class BabyStateScreen extends Screen implements Observer{
    /********** DECLARATIONS **********/
    // Variables
    static private final double QUIET = 1;
    static private final double NOISY = 2;
    static private final double AGITATED = 3;
    private long totalQuiet, totalNoise, totalAgitated;
    private long radioButtonSelected;
    private long totalTime, firstRadioValue, secondRadioValue;
    private boolean firstButtonIsVisible, secondButtonIsVisible;

    // Widget of the view
    private TimelineView timelineView;
    private TextView current_state, noisy_time, quiet_time, agitated_time, state_verb, baby_name, time_start, time_3_4, time_middle, time_1_4, progressBarValue;
    private RadioButton radioButton_overview_1, radioButton_2, radioButton_3, radioButton_4;
    private View radioButtonGroup;
    private SeekBar seekbar;

    // Handler
    private final Handler myHandler = new Handler();

    /************ METHODS ************/
    /**
     * \brief Constructor
     */
    public BabyStateScreen() {
        super(R.layout.baby_state_screen,false,false);
        totalQuiet=0;
        totalNoise=0;
        totalAgitated=0;
        radioButtonSelected=0;
        firstButtonIsVisible=false;
        secondButtonIsVisible=false;
    }

    /**
     * \brief Change the text when baby is quiet
     */
    public void stateQuiet(){
        state_verb.setText("\tis\t");
        current_state.setText("quiet");
        current_state.setTextColor(Color.parseColor("#34B536"));
    }

    /**
     * \brief Change the text when baby is noisy
     */
    public void stateNoisy(){
        state_verb.setText("\tmakes\t");
        current_state.setText("noise");
        current_state.setTextColor(Color.parseColor("#E83040"));
    }

    /**
     * \brief Change the text when baby is agitated
     */
    public void stateAgitated (){
        state_verb.setText("\tis\t");
        current_state.setText("agitated");
        current_state.setTextColor(Color.parseColor("#FC843A"));
    }

    /**
     * \brief Change the quiet time in the text view
     * \param time  Time in milliseconds
     */
    public void timeQuiet(long time) {
        quiet_time.setText(transformTimeMilliseconds(time, 1.0,false));
    }
    /**
     * Change the noisy time in the text view
     * \param time  Time in milliseconds
     */
    public void timeNoisy(long time){
        noisy_time.setText(transformTimeMilliseconds(time, 1.0,false));
    }

    /**
     * Change the agitated time in the text view
     * \param time  Time in milliseconds
     */
    public void timeAgitated(long time){
        agitated_time.setText(transformTimeMilliseconds(time, 1.0,false));

    }

    /**
     * \brief Change the time step (time below the timeline)
     * \param time  Time in milliseconds
     */
    public void timeStep(long time){
        time_start.setText(transformTimeMilliseconds(time, 1.0, true));
        time_1_4.setText(transformTimeMilliseconds(time, 0.25, true));
        time_middle.setText(transformTimeMilliseconds(time, 0.5, true));
        time_3_4.setText(transformTimeMilliseconds(time, 0.75, true));

    }

    /**
     * \brief Handler to change the information visibility
     * \param isConnected The connection state
     */
    public void changeVisibility(final boolean isConnected){
        myHandler.post(new Runnable() {
            @Override
            public void run() {
             updateVisibility(isConnected);
            }
        });
    }

    /**
     * \brief Change the information visibility
     * \param isConnected   The connection state
     */
    private void updateVisibility(boolean isConnected){
        if(isConnected){
            baby_name.setVisibility(View.VISIBLE);
            state_verb.setVisibility(View.VISIBLE);
            current_state.setVisibility(View.VISIBLE);
        }
        else{
            baby_name.setVisibility(View.INVISIBLE);
            state_verb.setVisibility(View.INVISIBLE);
            current_state.setVisibility(View.INVISIBLE);
        }
        radioButton_overview_1.setVisibility(View.VISIBLE);
        time_start.setVisibility(View.VISIBLE);
        time_middle.setVisibility(View.VISIBLE);
        time_1_4.setVisibility(View.VISIBLE);
        time_3_4.setVisibility(View.VISIBLE);
        radioButton_overview_1.setVisibility(View.VISIBLE);
        radioButton_4.setVisibility(View.VISIBLE);
    }

    /**
     * \brief Handler to change the text view when baby is quiet
     */
    public void displayStateQuiet(){
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                stateQuiet();
            }
        });
    }

    /**
     * \brief Handler to change the text view when baby is noisy
     */
    public void displayStateNoisy(){
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                stateNoisy();
            }
        });
    }

    /**
     * \brief Handler to change the text view when baby is agitated
     */
    public void displayStateAgitated(){
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                stateAgitated();
            }
        });
    }

    /**
     * \brief Handler to change the quiet time in the text view
     */
    public void displayTimeQuiet(final long time){
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                timeQuiet(time);
            }
        });
    }

    /**
     * \brief Handler to change the noisy time in the text view
     */
    public void displayTimeNoisy(final long time){
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                timeNoisy(time);
            }
        });
    }

    /**
     * \brief Handler to change the agitated time in the text view
     */
    public void displayTimeAgitated(final long time){
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                timeAgitated(time);
            }
        });
    }

    /**
     * \brief Handler to change the step time below the timeline
     */
    public void displayTimeStep(final long time){
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                     timeStep(time);
            }
        });
    }

    /**
     * \brief Transform the time in milliseconds in hours, minutes and seconds
     * \param totalMs   The total time in milliseconds
     * \param coef  The coefficient
     * \param iWwantSecDisplay  Treat the seconds
     */
    public String transformTimeMilliseconds(long totalMs, double coef, boolean iWwantSecDisplay){
        double time = coef*(double)totalMs;
        long totalTime = (long)time;
        totalTime = totalTime/1000;
        long sec = totalTime%60;
        totalTime=totalTime/60;
        long min=totalTime%60;
        totalTime=totalTime/60;
        long hour = totalTime;
        if (iWwantSecDisplay){
            if (hour == 0 && min ==0) {
                if(sec != 0) {
                    return ("    " + String.format("%02d", sec) + " \"");
                }
                else{
                    return "   < 1\" ";
                }
            }
            else{
                return (String.format("%02d", hour) + ":" + String.format("%02d", min)+ ":" + String.format("%02d", sec));
            }
        }
        else {
            return (String.format("%02d", hour) + ":" + String.format("%02d", min) + ":" + String.format("%02d", sec));
        }
    }

    /**
     * \brief Display the state by using the handler if it is necessary
     * \param state The last state in the array list
     * \param onHandler True if it is a new state received by the observer pattern
     */
    private void displayGoodState(double state, boolean byUpdate){
        if (state == NOISY){
            if(byUpdate) {
                displayStateNoisy();
            }
            else{
                stateNoisy();
            }
        }
        else if (state == AGITATED){
            if(byUpdate) {
                displayStateAgitated();
            }
            else{
                stateAgitated();
            }
        }
        else if (state == QUIET){
            if(byUpdate) {
                displayStateQuiet();
            }
            else{
                stateQuiet();
            }
        }
    }

    /**
     * \brief Give the total time for each state
     * \param mArrayList    The array list
     * \return  The total time in milliseconds
     */
    private long[] giveMeTotalTime( ArrayList<ProgressItem> mArrayList){
        long[] time = new long[4];
        for (int i=0; i< mArrayList.size(); i++){
            if (mArrayList.get(i).state == QUIET){
                 time[0] += mArrayList.get(i).time;
             }
            else if (mArrayList.get(i).state == NOISY){
                time[1] += mArrayList.get(i).time;
            }
            else if(mArrayList.get(i).state == AGITATED){
                time[2] += mArrayList.get(i).time;
            }
            else{
                time[3] += mArrayList.get(i).time;
            }
        }
       return time;
    }

    /**
     * \brief Notification by an observable
     * \param obs   The observable class
     * \param obj   The value
     */
    public void update(Observable obs, Object obj) {
        ArrayList<ProgressItem> mArrayList = (ArrayList<ProgressItem>) obj;
        long[] time;
        if (mArrayList.size() > 0) {
            timelineView.redraws(mArrayList);
            this.displayGoodState(mArrayList.get(mArrayList.size() - 1).state,true);
            time = giveMeTotalTime(mArrayList);
            displayTimeQuiet(time[0]);
            displayTimeNoisy(time[1]);
            displayTimeAgitated(time[2]);
            setTotalTime(time[0]+time[1]+time[2]+time[3]);
            if (radioButtonSelected == 0){
                displayTimeStep(totalTime);
            }
            if (totalTime > firstRadioValue && !firstButtonIsVisible) {
                radioButton_2.setVisibility(View.VISIBLE);
                firstButtonIsVisible =true;
            }
            if (totalTime > secondRadioValue && !secondButtonIsVisible) {
                radioButton_3.setVisibility(View.VISIBLE);
                secondButtonIsVisible =true;
            }
        }
    }

    /**
     * \brief Customize the presentation of radio button
     * \param isCustom  If the radio button selected is the custom seekBar
     */
    public void customButtonAvailable(boolean isCustom){
        if(isCustom) {
            setMargins(radioButtonGroup, 70, 30, 0, 0);
            setMargins(radioButton_overview_1,0,0,5,0);
            if(radioButton_2 !=null) {
                setMargins(radioButton_2, 0, 0, 5, 0);
            }
            if(radioButton_3 !=null) {
                setMargins(radioButton_3, 0, 0, 5, 0);
            }
            seekbar.setVisibility(View.VISIBLE);
            progressBarValue.setVisibility(View.VISIBLE);
        }else {
            setMargins(radioButtonGroup, 250, 30, 0, 0);
            setMargins(radioButton_overview_1,0,0,60,0);
            if(radioButton_2 !=null) {
                setMargins(radioButton_2, 0, 0, 60, 0);
            }
            if(radioButton_3 !=null) {
                setMargins(radioButton_3, 0, 0, 60, 0);
            }
            seekbar.setVisibility(View.GONE);
            progressBarValue.setVisibility(View.GONE);
        }
    }

    /**
     * \brief Modification of the margin of widgets
     * \param v The view
     * \param l Margin left
     * \param t Margin top
     * \param r Margin right
     * \param b Margin bottom
     */
    private void setMargins (View v, int l, int t, int r, int b) {
        if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
            ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
            p.setMargins(l, t, r, b);
            v.requestLayout();
        }
    }

    /**
     * \brief Handler to display the good time in the progress bar
     * \param progressValue The value of the seek bar
     */
    public void displayValueOnProgressBar(final int progressValue){
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                String val;
                int posX ;
                if((totalTime * (progressValue/100.0) < 1000)){
                    val = " 01 \"";
                    displayTimeStep((long)1000);
                }else{
                    val = transformTimeMilliseconds((long)(totalTime * (progressValue/100.0)),1.0 ,true);
                    displayTimeStep((long)(totalTime * (progressValue/100.0)));
                }
                posX =(int)((seekbar.getWidth()*(progressValue/100.0)*0.97));
                progressBarValue.setText(val);
                setMargins(progressBarValue,posX,5,0,0);
            }
        });
    }

    /**
     * \brief Show the timeline with all the information
     * \param mArrayList    The list of state
     */
    private void displayFirstTimeline(ArrayList<ProgressItem> mArrayList) {
        long[] time;
        if (mArrayList.size() > 0) {
            timelineView.redraws(mArrayList);
            this.displayGoodState(mArrayList.get(mArrayList.size() - 1).state,false);
            time = giveMeTotalTime(mArrayList);
            timeQuiet(time[0]);
            timeNoisy(time[1]);
            timeAgitated(time[2]);
            setTotalTime(time[0]+time[1]+time[2]);
            timeStep(totalTime);
        }
    }

    /**
     * \brief Change the title of the scree to know if we are connected
     * \param isConnected   True if it is connected to Nanny bear
     */
    public void changeConnectionStatus(boolean isConnected){
        if(isConnected){
            baby_name.setVisibility(View.VISIBLE);
            state_verb.setVisibility(View.VISIBLE);
            current_state.setVisibility(View.VISIBLE);
        }
        else{
            baby_name.setVisibility(View.INVISIBLE);
            state_verb.setVisibility(View.INVISIBLE);
            current_state.setVisibility(View.INVISIBLE);
        }
    }

    /**
     * \brief Update the view
     * \param mArrayList    The list of states
     * \param babyName  The baby name
     * \param isConnected   The state connection
     */
    public void updateView(ArrayList<ProgressItem> mArrayList, String babyName, boolean isConnected){
        displayFirstTimeline(mArrayList);
        baby_name.setText(babyName);
        updateVisibility(isConnected);
    }

    /**
     * \brief Show the view
     * \param thresholdTemp The value of temperature threshold
     * \param thresholdHum  The value of humidity threshold
     */
    public void display(int thresholdTemp, int thresholdHum){
        getBabyStateButton().setBackgroundResource(R.drawable.sound_active);
        getTempButton().setBackgroundResource(getTempDrawable()[thresholdTemp]);
        getHumidityButton().setBackgroundResource(getHumDrawable()[thresholdHum]);
    }

    /**
     * \brief Close the view (empty)
     */
    public void close() {
    }

    /*********** ACCESSORS ***********/
    /**
     * \brief Get method for the total time
     */
    public long getTotalTime() {
        return totalTime;
    }

    /**
     * \brief Get method for the third radio button
     */
    public RadioButton getRadioButton_3() {
        return radioButton_3;
    }

    /**
     * \brief Get method for the second radio button
     */
    public RadioButton getRadioButton_2() {
        return radioButton_2;
    }

    /**
     * \brief Set method for the current state (TextView)
     */
    public void setCurrent_state(TextView current_state) {
        this.current_state = current_state;
    }

    /**
     * \brief Set method for the noisy time (TextView)
     */
    public void setNoisy_time(TextView noisy_time) {
        this.noisy_time = noisy_time;
    }

    /**
     * \brief Set method for the agitated time (TextView)
     */
    public void setAgitated_time(TextView agitated_time) {
        this.agitated_time = agitated_time;
    }

    /**
     * \brief Set method for the quiet time (TextView)
     */
    public void setQuiet_time(TextView quiet_time) {
        this.quiet_time = quiet_time;
    }

    /**
     * \brief Set method to change the progress bar value
     */
    public void setProgressBarValue(TextView progressBarValue) {
        this.progressBarValue = progressBarValue;
    }

    /**
     * \brief Set method for the state verb (TextView)
     */
    public void setState_verb(TextView state_verb) {
        this.state_verb = state_verb;
    }

    /**
     * \brief Set method for the baby name (TextView)
     */
    public void setBaby_name(TextView baby_name) {
        this.baby_name = baby_name;
    }

    /**
     * \brief Set method for the radio button overview
     */
    public void setRadioButton_overview_1(RadioButton radioButton_overview_1) {
        this.radioButton_overview_1 = radioButton_overview_1;
    }

    /**
     * \brief Set method for the radio button 2
     */
    public void setRadioButton_2(RadioButton radioButton_2) {
        this.radioButton_2 = radioButton_2;
    }

    /**
     * \brief Set method for the radio button 3
     */
    public void setRadioButton_3(RadioButton radioButton_3) {
        this.radioButton_3 = radioButton_3;
    }

    /**
     * \brief Set method for the radio button customize
     */
    public void setRadioButton_4(RadioButton radioButton_4) {
        this.radioButton_4 = radioButton_4;
    }

    /**
     * \brief Set method for the start time (TextView)
     */
    public void setTime_start(TextView time_start) {
        this.time_start = time_start;
    }

    /**
     * \brief Set method for the time 3/4 (TextView)
     */
    public void setTime_3_4(TextView time_3_4) {
        this.time_3_4 = time_3_4;
    }

    /**
     * \brief Set method for the middle time (TextView)
     */
    public void setTime_middle(TextView time_middle) {
        this.time_middle = time_middle;
    }

    /**
     * \brief Set method for the time 1/4 (TextView)
     */
    public void setTime_1_4(TextView time_1_4) {
        this.time_1_4 = time_1_4;
    }

    /**
     * \brief Set method for the radio button selected
     */
    public void setRadioButtonSelected(long radioButtonSelected) {
        this.radioButtonSelected = radioButtonSelected;
    }

    /**
     * \brief Set method for the timeline
     */
    public void setTimelineView(TimelineView timelineView) {
        this.timelineView = timelineView;
    }

    /**
     * \brief Set method for the total time
     */
    public void setTotalTime(long totalTime) {
        this.totalTime = totalTime;
    }

    /**
     * \brief Set method for the radioButton group
     */
    public void setRadioButtonGroup(View radioButtonGroup) {
        this.radioButtonGroup = radioButtonGroup;
    }

    /**
     * \brief Set method for the seekbar
     */
    public void setSeekbar(SeekBar seekbar) {
        this.seekbar = seekbar;
    }

    /**
     * \brief Set method for the time of the first radio button
     */
    public void setFirstRadioValue(long firstRadioValue) {
        this.firstRadioValue = firstRadioValue;
    }

    /**
     * \brief Set method for the time of the second radioButton
     */
    public void setSecondRadioValue(long secondRadioValue) {
        this.secondRadioValue = secondRadioValue;
    }

    /**
     * \brief Set method for the visibility of the second button
     */
    public void setSecondButtonIsVisible(boolean secondButtonIsVisible) {
        this.secondButtonIsVisible = secondButtonIsVisible;
    }

    /**
     * \brief Set method for the visibility of the first button
     */
    public void setFirstButtonIsVisible(boolean firstButtonIsVisible) {
        this.firstButtonIsVisible = firstButtonIsVisible;
    }
}
